/**
 * Project: lionbule-other
 * 
 * File Created at 2012-4-6
 * $Id$
 * 
 * Copyright 1999-2100 Alibaba.com Corporation Limited.
 * All rights reserved.
 *
 * This software is the confidential and proprietary information of
 * Alibaba Company. ("Confidential Information").  You shall not
 * disclose such Confidential Information and shall use it only in
 * accordance with the terms of the license agreement you entered into
 * with Alibaba.com.
 */
package com.verfy.parser;

import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.Map.Entry;
import java.util.regex.Matcher;
import java.util.regex.Pattern;

/**
 * ExpressionParser.
 * <p>
 * \\d+(?:\\.\\d+)?
 * </p>
 * 
 * @author LionBule 2012-4-6 上午10:00:15
 */
public class ExpressionParser {
    private static final char    BRACE_LEFT     = '{';
    private static final char    BRACE_RIGHT    = '}';

    private static final String  IN             = "in";
    private static final String  NOT_IN         = "notin";

    private static final Pattern NUMBER_PATTERN = Pattern.compile("\\d+(?:\\.\\d+)?");

    private static final Pattern G_AND_L        = Pattern
                                                        .compile("\\(\\d+(?:\\.\\d+)?,\\d+(?:\\.\\d+)?\\)");
    private static final Pattern G_AND_LE       = Pattern
                                                        .compile("\\(\\d+(?:\\.\\d+)?,\\d+(?:\\.\\d+)?\\]");
    private static final Pattern GE_AND_L       = Pattern
                                                        .compile("\\[\\d+(?:\\.\\d+)?,\\d+(?:\\.\\d+)?\\)");
    private static final Pattern GE_AND_LE      = Pattern
                                                        .compile("\\[\\d+(?:\\.\\d+)?,\\d+(?:\\.\\d+)?\\]");

    private static final Pattern L_OR_G         = Pattern
                                                        .compile("\\(∞,\\d+(?:\\.\\d+)?\\)\\|\\|\\(\\d+(?:\\.\\d+)?,∞\\)");
    private static final Pattern L_OR_GE        = Pattern
                                                        .compile("\\(∞,\\d+(?:\\.\\d+)?\\)\\|\\|\\[\\d+(?:\\.\\d+)?,∞\\)");
    private static final Pattern LE_OR_G        = Pattern
                                                        .compile("\\(∞,\\d+(?:\\.\\d+)?\\]\\|\\|\\(\\d+(?:\\.\\d+)?,∞\\)");
    private static final Pattern LE_OR_GE       = Pattern
                                                        .compile("\\(∞,\\d+(?:\\.\\d+)?\\]\\|\\|\\[\\d+(?:\\.\\d+)?,∞\\)");

    private static final Pattern GREATE         = Pattern.compile("\\(\\d+(?:\\.\\d+)?,∞\\)");
    private static final Pattern LESS           = Pattern.compile("\\(∞,\\d+(?:\\.\\d+)?\\)");
    private static final Pattern GE             = Pattern.compile("\\[\\d+(?:\\.\\d+)?,∞\\)");
    private static final Pattern LE             = Pattern.compile("\\(∞,\\d+(?:\\.\\d+)?\\]");

    private final String         exp;

    public ExpressionParser(final String exp) {
        this.exp = exp;
    }

    public Expression parser() {
        if (exp == null || exp.trim().isEmpty()) {
            return new DefaultExpression();
        }

        //二元表达式解析
        Expression exp = initDualExp();

        //一元表达式解析
        if (exp == null) {
            exp = initUnitaryExp();
        }

        //选择表达式解析
        if (exp == null) {
            exp = initOptionExp();
        }

        //异常
        if (exp == null) {
            throw new GrammarException();
        }

        return exp;
    }

    private Expression initDualExp() {
        Map<Integer, Pattern> pats = new HashMap<Integer, Pattern>();
        pats.put(NumberExpression.G_AND_L, G_AND_L);
        pats.put(NumberExpression.G_AND_LE, G_AND_LE);
        pats.put(NumberExpression.GE_AND_L, GE_AND_L);
        pats.put(NumberExpression.GE_AND_LE, GE_AND_LE);

        pats.put(NumberExpression.L_OR_G, L_OR_G);
        pats.put(NumberExpression.L_OR_GE, L_OR_GE);
        pats.put(NumberExpression.LE_OR_G, LE_OR_G);
        pats.put(NumberExpression.LE_OR_GE, LE_OR_GE);

        for (Entry<Integer, Pattern> p : pats.entrySet()) {
            Matcher m = p.getValue().matcher(exp);
            if (m.matches()) {
                Matcher nums = NUMBER_PATTERN.matcher(exp);
                double[] argument = new double[2];
                for (int i = 0; nums.find(); i++) {
                    argument[i] = Double.valueOf(nums.group());
                }

                return new NumberExpression(argument[0], argument[1], p.getKey());
            }
        }

        return null;
    }

    private Expression initUnitaryExp() {
        Map<Integer, Pattern> pats = new HashMap<Integer, Pattern>();
        pats.put(NumberExpression.GREATE, GREATE);
        pats.put(NumberExpression.LESS, LESS);
        pats.put(NumberExpression.GE, GE);
        pats.put(NumberExpression.LE, LE);

        for (Entry<Integer, Pattern> p : pats.entrySet()) {
            Matcher m = p.getValue().matcher(exp);
            if (m.matches()) {
                Matcher nums = NUMBER_PATTERN.matcher(exp);
                if (nums.find()) {
                    double argument = Double.valueOf(nums.group());
                    return new NumberExpression(argument, p.getKey());
                } else {
                    throw new GrammarException("expression is error. exp=" + exp);
                }
            }
        }

        return null;
    }

    private Expression initOptionExp() {
        int start = exp.indexOf(BRACE_LEFT);
        int end = exp.indexOf(BRACE_RIGHT);
        String dataStr = exp.substring(start + 1, end).replaceAll("\"", "");//去除双引号
        dataStr = dataStr.replaceAll("\'", "");//去除单引号
        String[] value = dataStr.split(",");

        //去除左右空格及数据类型转换
        List<String> dataList = new ArrayList<String>(value.length);
        for (String s : value) {
            dataList.add(s.trim());
        }

        if (exp.startsWith(IN)) {
            return new OptionExpression(OptionExpression.IN, dataList);
        } else if (exp.startsWith(NOT_IN)) {
            return new OptionExpression(OptionExpression.NOT_IN, dataList);
        }

        return null;
    }

}
